#include "temperature.h"
#include "usart.h"

PID_TypeDef pid[4]; // PID控制器实例
uint16_t adc_value[8]; // 存储ADC读数的数组
float current_temp [4], target_temp[4]; // 存储温度的数组
uint16_t pid_output[4]; // 存储PID输出的数组

/**
 * @brief 将ADC值转换为NTC热敏电阻的温度 
 * @note 高侧分压电阻 10K Ohm 0.1%, SDNT2012X103F3950FTF 10K Ohm 1%
 * @param adc_value STM32 12bit ADC读数，范围0-4095
 * @return 温度（单位：摄氏度）
 */
float NTC_Temperature_Convert(uint16_t adc_value)
{
  // NTC的B值
  const uint16_t B = 3950;
  // ADC的满量程最大值（12bit ADC）
  const uint16_t adc_max = 0xfff; // 4095

  // 计算电阻比值 k = R / R0 (R0为25摄氏度时10K Ohm)
  float k = (float) adc_value / (float) (adc_max - adc_value);

  // 根据B值和电阻比值计算温度
  float T = (float) (B * 298.15) / (float) (298.15 * logf(k) + B);

  // 以摄氏度返回计算得到的温度值
  return T - 273.15;
}

/**
 * @brief 设置加热通道的PWM输出
 * @param ch1 通道1的PWM占空比（0-1000）
 * @param ch2 通道2的PWM占空比（0-1000）
 * @param ch3 通道3的PWM占空比（0-1000）
 * @param ch4 通道4的PWM占空比（0-1000）
 */
void Heater_Channel_Set(uint16_t ch1, uint16_t ch2, uint16_t ch3, uint16_t ch4)
{
    if (ch1 > 1000) ch1 = 1000;
    if (ch2 > 1000) ch2 = 1000;
    if (ch3 > 1000) ch3 = 1000;
    if (ch4 > 1000) ch4 = 1000;
    __HAL_TIM_SET_COMPARE(&htim3, TIM_CHANNEL_1, ch1 * 10); //NMOS1
    __HAL_TIM_SET_COMPARE(&htim3, TIM_CHANNEL_2, ch2 * 10); //NMOS2
    __HAL_TIM_SET_COMPARE(&htim3, TIM_CHANNEL_3, ch3 * 10); //NMOS3
    __HAL_TIM_SET_COMPARE(&htim3, TIM_CHANNEL_4, ch4 * 10); //NMOS4
}

/**
 * @brief 初始化温度控制系统
 * @note 打开加热通道的PWM输出和PID控制器，打开ADC转换
 */
void Temperature_Init(void)
{
    // 启动加热通道的PWM输出
    HAL_TIM_PWM_Start(&htim3, TIM_CHANNEL_1); //NMOS1
    HAL_TIM_PWM_Start(&htim3, TIM_CHANNEL_2); //NMOS2
    HAL_TIM_PWM_Start(&htim3, TIM_CHANNEL_3); //NMOS3
    HAL_TIM_PWM_Start(&htim3, TIM_CHANNEL_4); //NMOS4
    // 初始化加热通道的PWM输出
    Heater_Channel_Set(0, 0, 0, 0); // 设置初始占空比为0

    // 初始化每个通道的PID控制器
    for (int i = 0; i < 4; i++) {
        pid[i].mode = POSITION_PID; // 设置为位置式PID
        pid[i].Kp = 0.0f; // 设置比例系数
        pid[i].Ki = 0.0f; // 设置积分系数
        pid[i].Kd = 0.0f; // 设置微分系数
        pid[i].output_limit = 1000.0f; // 设置输出限制 +-1000
        pid[i].integral_limit = 500.0f; // 设置积分限幅
        pid[i].deadband = -1.0f; // 设置死区
    }
    // 将目标温度初始化为0
    for (int i = 0; i < 4; i++) {
        target_temp[i] = 0.0f; // 初始化目标温度为0
        pid_output[i] = 0; // 初始化PID输出为0
    }
}

/**
 * @brief 设置PID控制器参数
 * @param pid_param 包含PID参数的数组，格式为 [Kp, Ki, Kd]
 * @note 该函数会将传入的PID参数应用到所有4个通道的PID控制器上
 */
void Temperature_PID_Set(float pid_param[3])
{
    for (int i = 0; i < 4; i++) {
        pid[i].Kp = pid_param[0];
        pid[i].Ki = pid_param[1];
        pid[i].Kd = pid_param[2];
        pid[i].pos_out= 0.0f; // 重置位置输出
        pid[i].delta_out[0] = 0.0f; // 重置增量输出
        pid[i].delta_out[1] = 0.0f; // 重置增量输出
    }
}

void Temperature_Target_Set(float temp[4])
{
    for (int i = 0; i < 4; i++) {
        if (target_temp[i] < 0.0f) {
            target_temp[i] = 0.0f; // 确保目标温度不小于0
        }
        target_temp[i] = temp[i];
    }
}

/**
 * @brief 温度控制函数
 * @note 该函数会计算每个通道的平均温度，并根据PID控制器计算输出值
 *      然后设置加热通道的PWM输出，最后重新启动ADC DMA转换
 */
void Temperature_Control(void)
{   
    float pid_offset = 0.0f; // PID输出偏移量

    // ADC开启转换，预计需要122us完成
    HAL_ADC_Start_DMA(&hadc1, (uint32_t *)adc_value, 8); // 启动ADC DMA
    HAL_ADC_PollForConversion(&hadc1, 5); // 等待转换完成

    for (int i = 0; i < 4; i++) {
        current_temp[i] = NTC_Temperature_Convert(adc_value[i * 2]) + 
                            NTC_Temperature_Convert(adc_value[i * 2 + 1]); // 计算平均温度
        current_temp[i] *= 0.5f; // 计算平均温度
        // 计算PID输出 带有一定的偏移量
        pid_output[i] =(uint16_t) (pid_offset + PID_Calculate(&pid[i], current_temp[i], target_temp[i]));
    }
    // 设置加热通道的PWM输出
    Heater_Channel_Set(pid_output[0],pid_output[1],pid_output[2],pid_output[3]);
}

/**
 * @brief 将温度信息写入JSON格式
 * @param json cJSON对象，用于存储温度信息
 * @note 该函数会将当前温度、PID输出和时间戳添加到JSON对象中
 */
void Temperature_Info_WriteCmd(uint8_t *cmd_data, uint16_t len)
{
    if (len < 21) return; // 确保缓冲区足够大
    cmd_data[0] = 0x33; // 命令ID
    int16_t temp[4];
    for (int16_t i = 0; i < 4; i++) {
        temp[i] = (int16_t)(current_temp[i] * 100.0f); // 将温度转换为整数形式
        memcpy(cmd_data + 1 + i * 2, temp + i, sizeof(temp[i])); // 将温度数据复制到命令数据中
        memcpy(cmd_data + 9 + i * 2, pid_output + i, sizeof(pid_output[i])); // 将PID输出数据复制到命令数据中
    }
    uint32_t timestamp = osKernelGetTickCount(); // 时间戳
    memcpy(cmd_data + 17, &timestamp, 4); // 将时间戳复制到命令数据中
}